/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.view.component.applet;

import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import javax.faces.component.UIComponent;
import javax.faces.component.UIParameter;
import javax.faces.context.ExternalContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.http.HttpServletRequest;
import offset.nodes.client.view.BaseApplet;
import offset.nodes.server.config.NodesConfig;
import offset.nodes.server.view.LayoutBean;

public class AppletComponent extends UIComponentBase {

    public static final String APPLET_SCRIPT_PATH = "/common/applet.js";
    public static final String DEPLOY_JAVA_SCRIPT_PATH = "/common/deployJava.js";
    public static final String PAR_ELEMENT_ID = "elementId";
    public static final String PAR_FUNCTION = "function";
    public static final String PAR_GENERATE_CODE = "generateCode";
    public static final String PAR_APPLET_ID = "appletId";
    public static final String PAR_CODE = "code";
    public static final String PAR_CODEBASE = "codebase";
    public static final String PAR_JAR = "jar";
    public static final String PAR_WIDTH = "width";
    public static final String PAR_HEIGHT = "height";
    public static final String PAR_VERSION = "jre";

    public void encodeBegin(FacesContext context) throws IOException {
        if (!isRendered())
            return;

        ResponseWriter writer = context.getResponseWriter();

        String version = (String) getAttributes().get(PAR_VERSION);
        if (version != null) {
            writer.startElement("script", this);
            writer.writeAttribute("type", "text/javascript", null);
            writer.writeAttribute("src", getURLPath(DEPLOY_JAVA_SCRIPT_PATH), null);
            writer.endElement("script");
            writer.write("\n");
        }

        writer.startElement("script", this);
        writer.writeAttribute("type", "text/javascript", null);
        writer.writeAttribute("src", getURLPath(APPLET_SCRIPT_PATH), null);
        writer.endElement("script");
        writer.write("\n");

        writer.startElement("script", this);
        writer.writeAttribute("type", "text/javascript", null);
        writer.writeText(encodeText(), null);
        writer.endElement("script");
        writer.write("\n");
    }

    protected String encodeText() {
        StringBuffer buf = new StringBuffer();

        String version = (String) getAttributes().get(PAR_VERSION);
        if (version != null)
            buf.append("\ndeployJava.verifyJRE('" + version + "');\n");

        String elementId = (String) getAttributes().get(PAR_ELEMENT_ID);
        String function = (String) getAttributes().get(PAR_FUNCTION);
        String generateCode = (String) getAttributes().get(PAR_GENERATE_CODE);
        
        if (elementId != null) {
            buf.append("function ");
            if (function == null)
                function = elementId;
            buf.append(function + "() {\n");
            buf.append("n_substituteApplet(\n");
            buf.append("'elementId', '" + determineClientId(elementId) + "',\n");
            buf.append(encodeCommonParameters());
            buf.append("\n);\n");
            buf.append("}");
        } else if (function != null) {
            buf.append("function ");
            buf.append(function + "() {\n");
            buf.append("n_substituteApplet(\n");
            buf.append("'elementId', '" + LayoutBean.ELEMENT_ID_APPLET + "',\n");
            buf.append(encodeCommonParameters());
            buf.append("\n);\n");
            buf.append("}");
        } else if (generateCode != null) {
            buf.append("function ");
            buf.append(generateCode + "() {\n");
            buf.append("return new Array(\n");
            buf.append(encodeCommonParameters());
            buf.append(");\n");
            buf.append("}");
        } else {
            buf.append("n_addApplet(\n");
            String id = (String) getAttributes().get(PAR_APPLET_ID);
            if (id != null)
                buf.append("'id', '" + id + "',\n");
            buf.append(encodeCommonParameters());
            buf.append("\n);");
        }

        return buf.toString();
    }

    protected String getURLPath(String filePath) {
        ExternalContext context = (ExternalContext) FacesContext.getCurrentInstance().getExternalContext();
        return context.getRequestContextPath() + filePath;
    }

    /**
     * This attempts to determine the client id from the id of the component, that shall be
     * substituted. It assumes, that this component will be in the same container as the
     * AppletComponent.
     *
     * @param id The id of the component to be substituted by javascript
     * @return the clientId of that component, hopefully
     */
    protected String determineClientId(String id) {
        String clientId = getClientId(FacesContext.getCurrentInstance());
        String prefix = clientId.substring(0, clientId.lastIndexOf(":"));
        return prefix + ":" + id;
    }

    protected String encodeCommonParameters() {
        StringBuffer buf = new StringBuffer();

        String code = (String) getAttributes().get(PAR_CODE);
        buf.append("'code', '" + code + "',\n");
        String codebase = (String) getAttributes().get(PAR_CODEBASE);
        buf.append("'codebase', '" + codebase + "',\n");
        String width = (String) getAttributes().get(PAR_WIDTH);
        if (width != null)
            buf.append("'width', '" + width + "',\n");
        String height = (String) getAttributes().get(PAR_HEIGHT);
        if (height != null)
            buf.append("'height', '" + height + "',\n");

        Parameter[] parameters = getParameters();
        for (int i = 0; i < parameters.length; i++) {
            buf.append("'" + parameters[i].getName() + "','" + parameters[i].getValue() + "',\n");
        }

        Locale userLocale = ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getLocale();
        buf.append("'" + BaseApplet.PAR_LOCALE + "','" + userLocale.getLanguage() + BaseApplet.SEPARATOR_LOCALE + userLocale.getCountry() + "',\n");

        parameters = getJarDependencyParameters();
        for (int i = 0; i < parameters.length; i++) {
            buf.append("'" + parameters[i].getName() + "','" + parameters[i].getValue() + "'");
            if (i < parameters.length - 1)
                buf.append(",\n");
        }

        return buf.toString();
    }

    class Parameter {

        String name;
        String value;

        public Parameter(String name, String value) {
            this.name = name;
            this.value = value;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }

    protected Parameter[] getParameters() {
        if (getChildCount() > 0) {
            ArrayList<Parameter> parameterList = new ArrayList<Parameter>();

            for (UIComponent kid : getChildren()) {
                if (kid instanceof UIParameter) {
                    UIParameter uiParam = (UIParameter) kid;
                    Object value = uiParam.getValue();
                    Parameter param = new Parameter(uiParam.getName(),
                            (value == null ? null : value.toString()));
                    parameterList.add(param);
                }
            }
            return parameterList.toArray(new Parameter[parameterList.size()]);
        } else
            return new Parameter[0];
    }

    protected Parameter[] getJarDependencyParameters() {
        String jar = (String) getAttributes().get(PAR_JAR);
        AppletJarConfigElement element = NodesConfig.getInstance().getAppletJarConfig().getAppletJarConfigElement(jar);

        StringBuffer archives = new StringBuffer(element.getName());
        StringBuffer versions = new StringBuffer(element.getVersion());

        List<AppletJarConfigElement> dependencies = element.getDependencies();
        if (dependencies != null) {
            Iterator<AppletJarConfigElement> i = dependencies.iterator();
            while (i.hasNext()) {
                AppletJarConfigElement dependency = i.next();
                archives.append("," + dependency.getName());
                versions.append("," + dependency.getVersion());
            }
        }

        Parameter[] parameters = new Parameter[]{
            new Parameter("cache_option", "Plugin"),
            new Parameter("cache_archive", archives.toString()),
            new Parameter("cache_version", versions.toString())
        };

        return parameters;
    }

    public String getFamily() {
        return "appletFamily";
    }
}
